package com.zm.admin.utils;

import cn.hutool.core.util.ObjectUtil;
import com.zm.admin.zmeng.domain.ZmMonitor;
import com.zm.admin.zmeng.dto.monitor.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
 *
 * @Auther: MuYun
 * @Date: 2023/12/19 21:29
 * @Description：处理服务器监控脚本的返回结果
 *
 */
@Component
@Slf4j
public class ShellHandleUtil {

    // 在类中定义预编译的正则表达式
    private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+(\\.\\d+)?");

    @Value("${shell.path}")
    private String monitorPath;

    private static String monitorShellPath;

    /**
     * @Author MuYun
     * @Description  读取配置参数
     * @Date 22:02 2023/12/20
     * @Param []
     * @return void
     */
    @PostConstruct
    public void getShellPath() {
        monitorShellPath = this.monitorPath;
        log.info("-------------------------------------------{}",monitorShellPath);
    }

    /**
     * @Author MuYun
     * @Description  执行监控脚本，并将数据格式化
     * @Date 22:00 2023/12/20
     * @Param []
     * @return void
     */
    public static Map<String,Object> startMonitor(){
        Map<String,Object> map=new HashMap<>();
        //启动监控脚本，并拿到返回结果
        List<String> list = runShell(monitorShellPath);
        //拿到结果转化后的对象
        ZmMonitor zmMonitor = formatObj(list);
        map.put("obj",zmMonitor);
        Map<String, Object> dataMap = formatMonitorData(list);
        map.put("data",dataMap);
        return map;
    }

    /**
     * 运行指定路径的脚本，并返回执行结果
     * @param path
     * @return
     */
    public static List<String> runShell(String path){
        List<String> list=new ArrayList<>();
        Process process=null;
        BufferedReader bufrIn=null;
        try {
            // 执行命令, 返回一个子进程对象（命令在子进程中执行）
            ProcessBuilder processBuilder = new ProcessBuilder("/bin/sh", "-c", path);
            process = processBuilder.start();
            // 方法阻塞, 等待命令执行完成（成功会返回0）
            process.waitFor();
            // 获取命令执行结果,
            bufrIn = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
            // 读取输出
            String line = null;
            while ((line = bufrIn.readLine()) != null) {
                log.info("本行内容为:{}",line);
                list.add(line);
            }
        }catch (Exception e){
            log.error("脚本执行失败:{}",e);
        }finally {
            try {
                bufrIn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 销毁子进程
            if (process != null) {
                process.destroy();
            }
        }
        return list;
    }

    /**
     * 将脚本执行结果对应到对象的属性
     * @param list
     * @return
     */
    public static ZmMonitor formatObj(List<String> list){
        ZmMonitor zmMonitor=new ZmMonitor();
        zmMonitor.setTop(list.get(0));
        zmMonitor.setCpuInfo(list.get(1));
        zmMonitor.setSwapInfo(list.get(2));
        zmMonitor.setMemInfo(list.get(3));
        zmMonitor.setNetworking(list.get(4));
        zmMonitor.setTasks(list.get(5));
        //先拿最后一行：版号，因为不确定nginx的访问ip有没有5个
        zmMonitor.setVersion(list.get(list.size()-1));
        zmMonitor.setAccessNginx(list.get(6)+","+list.get(7)+","+list.get(8)+","+list.get(9)+","+list.get(10)+","+list.get(11)+","+list.get(12));
        //再拿访问nginx最多的前五ip
        List<String> subList=list.subList(0,list.size()-1).subList(13,list.size()-2);
        if(ObjectUtil.isNotEmpty(subList)){
            String ipToNginx="";
            for (int i = 0; i < subList.size(); i++) {
                ipToNginx+=subList.get(i);
                if(i!=subList.size()-1){
                    ipToNginx+="-";
                }
            }
            zmMonitor.setIpToNginx(ipToNginx );
        }
        return zmMonitor;
    }

        /*
        top - 10:48:39 up 179 days, 23:08,  2 users,  load average: 0.02, 0.24, 0.36
        %Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
        MiB Swap:   1025.0 total,    348.6 free,    676.4 used.    420.9 avail Mem
        MiB Mem :   1816.9 total,     86.6 free,   1246.8 used,    483.5 buff/cache
        200
        Tasks: 116 total,   2 running, 113 sleeping,   0 stopped,   1 zombie
        2023-12-19: 0
        2023-12-20: 0
        2023-12-21: 0
        2023-12-22: 0
        2023-12-23: 0
        2023-12-24: 0
        2023-12-25: 2
        300,123.56.155.157
        172,47.92.5.239
        138,112.30.115.56
        130,47.92.31.57
        128,47.92.73.109
        4.18.0-305.19.1.el8_4.x86_64
         */
    /**
     * @Author MuYun
     * @Description  数据格式化
     * @Date 19:58 2023/12/21
     * @Param []
     * @return java.util.Map<java.lang.String,java.lang.String>
     */
    public static Map<String,Object> formatMonitorData(List<String> list){
        //数据准备
        Map<String,Object> map=new HashMap<>();
        CpuInfo cpuInfo=new CpuInfo();
        SwapInfo swapInfo=new SwapInfo();
        MemInfo memInfo=new MemInfo();
        Tasks tasks=new Tasks();
        IpToNginx ipToNginx=new IpToNginx();
        ServerInfo serverInfo=new ServerInfo();
        /****************格式化开始****************/
        //服务器基本信息：运行时间及用户
        String[] splitArr = list.get(0).split(",");
        for (int i = 0; i < splitArr.length; i++) {
            String num = extrackNum(splitArr[i]);
            switch (i){
                case 0:
                    String[] split = splitArr[i].split("up");
                    num=extrackNum(split[1].trim());
                    serverInfo.setTimes(num);
                    break;
                case 2:
                    serverInfo.setUsers(num);
                    break;
                default:
                    break;
            }
        }
        //cpu信息
        String[] splitArr2 = list.get(1).split(",");
        for (int i = 0; i < splitArr2.length; i++) {
            String num = extrackNum(splitArr2[i].trim());
            switch (i){
                case 0:
                    cpuInfo.setUs(num);
                    break;
                case 1:
                    cpuInfo.setSy(num);
                    break;
                case 3:
                    cpuInfo.setId(num);
                    break;
                default:
                    break;
            }
        }
        map.put("cpu",cpuInfo);
        //磁盘信息
        String[] splitArr3 = list.get(2).split(",");
        for (int i = 0; i < splitArr3.length; i++) {
            String num = extrackNum(splitArr3[i].trim());
            switch (i){
                case 1:
                    swapInfo.setFree(num);
                    break;
                case 2:
                    swapInfo.setUsed(num);
                    break;
                default:
                    break;
            }
        }
        map.put("swap",swapInfo);
        //内存信息
        String[] splitArr4 = list.get(3).split(",");
        for (int i = 0; i < splitArr4.length; i++) {
            String num = extrackNum(splitArr4[i].trim());
            switch (i){
                case 1:
                    memInfo.setFree(num);
                    break;
                case 2:
                    memInfo.setUsed(num);
                    break;
                case 3:
                    memInfo.setCache(num);
                    break;
                default:
                    break;
            }
        }
        map.put("mem",memInfo);
        //网络情况
        String networking = list.get(4);
        serverInfo.setNetworking(networking);
        //进程信息
        String[] splitArr5 = list.get(5).split(",");
        for (int i = 0; i < splitArr5.length; i++) {
            String num = extrackNum(splitArr5[i].trim());
            switch (i){
                case 1:
                    tasks.setRunning(num);
                    break;
                case 2:
                    tasks.setSleeping(num);
                    break;
                case 3:
                    tasks.setStopped(num);
                    break;
                case 4:
                    tasks.setZombie(num);
                    break;
                default:
                    break;
            }
        }
        map.put("tasks",tasks);
        //nginx7日访问量
        List<String> visitNginx = list.subList(6, 13);
        List<String> retVisitNginx=new ArrayList<>();
        for (int i = 0; i < visitNginx.size(); i++) {
            retVisitNginx.add(visitNginx.get(i).split(":")[1].trim());
        }
        map.put("visitNginx",retVisitNginx);
        //访问nginx最多的前五ip及次数
        List<String> ipToNginxSubList = list.subList(13, list.size() - 1);
        List<String> ipList=new ArrayList<>();
        List<String> timesList=new ArrayList<>();
        if (ObjectUtil.isNotEmpty(ipToNginxSubList)) {
            for(String str:ipToNginxSubList){
                String[] splitArr6 = str.split(",");
                timesList.add(splitArr6[0].trim());
                ipList.add(splitArr6[1].trim());
            }
        }
        ipToNginx.setIp(ipList);
        ipToNginx.setTimes(timesList);
        map.put("ipToNginx",ipToNginx);
        //服务器基本信息：版本及架构
        String info = list.get(list.size() - 1);
        String[] splitArr7 = info.split("-");
        serverInfo.setVersion(splitArr7[0].trim());
        String[] splitArr8 = info.split("_");
        serverInfo.setStructure(splitArr8[splitArr8.length-1].trim());
        map.put("serverInfo",serverInfo);
        /****************格式化结束****************/
        return map;
    }

    /**
     * 从字符串中提取数字
     * @param str
     * @return
     */
    public static String extrackNum(String str){
        Matcher matcher = NUMBER_PATTERN.matcher(str);
        String number =null;
        while (matcher.find()) {
            number = matcher.group();
            log.info("提取出的数字:{}",number);
        }
        return number;
    }

}
